

## 定义
单一职责原则的英文是 Single Repository Principle ，缩写为 SRP。这个原则的英文描述是这样的：A class or module should have a single responsibility。

这个原则描述的对象包含两个，一个是类（class），一个是模块（module）。

:::tip
模块可以看作比类更加抽象的概念，类也可以看作模块。或者把模块看作比类更加粗粒度的代码块，模块中包含多个类，多个类组成一个模块。
:::

对于类来说，一个类只负责完成一个职责或者功能。也就是说，不要设计大而全的类，要设计粒度小、功能单一的类。


:::tip
一个类如果有多个职责或功能，就会增加被其它类依赖的可能性，增加了耦合度。
:::

比如一个类里面既包含订单的一些操作，又包含用户的一些操作。而订单和用户是两个独立的业务领域模型，我们将两个不相干的功能放到同一个类中，就违反了单一职责原则。


## 如何判断类的职责是否单一？

在真实的软件开发中，对于一个类是否职责单一的判定，是很难拿捏的。

在一个社交产品中，我们用下面的 UserInfo 类来记录用户的信息。你觉得，UserInfo 类的设计是否满足单一职责原则呢？

```java
public class UserInfo {
  private long userId;
  private String username;
  private String email;
  private String telephone;
  private long createTime;
  private long lastLoginTime;
  private String avatarUrl;
  private String provinceOfAddress; // 省
  private String cityOfAddress; // 市
  private String regionOfAddress; // 区
  private String detailedAddress; // 详细地址
  // ...省略其他属性和方法...
}
```
对于这个问题，有两种不同的观点。一种观点是，UserInfo 类包含的都是跟用户相关的信息，所有的属性和方法都隶属于用户这样一个业务模型，满足单一职责原则；另一种观点是，地址信息在 UserInfo 类中，所占的比重比较高，可以继续拆分成独立的 UserAddress 类，UserInfo 只保留除 Address 之外的其他信息，拆分之后的两个类的职责更加单一。


要从中做出选择，我们不能脱离具体的应用场景。如果在这个社交产品中，用户的地址信息跟其他信息一样，只是单纯地用来展示，那 UserInfo 现在的设计就是合理的。但是，如果这个社交产品发展得比较好，之后又在产品中添加了电商的模块，用户的地址信息还会用在电商物流中，那我们最好将地址信息从 UserInfo 中拆分出来，独立成用户物流信息（或者叫地址信息、收货信息等）。

所以，评价一个类的职责是否足够单一，并没有一个非常明确的、可以量化的标准。

实际上，在软件开发中，没有必要过度设计，<RedSpan>可以先设计一个粗粒度的类，满足业务需求。随着业务的发展，如果粗粒度的类越来越庞大，代码越来越多，这个时候，就可以将这个粗粒度的类，拆分成几个更细粒度的类。这就是所谓的持续重构。</RedSpan>


针对这个原则，有下面几条判断原则：
* 类中的代码行数、函数或属性过多，会影响代码的可读性和可维护性，就需要考虑对类进行拆分。
* 类依赖的其它类过多，或者依赖类的其它类过多，不符合高内聚、低耦合的设计思想，就需要考虑对类进行拆分。
* 私有方法过多，就需要考虑能否将私有方法独立到新的类中，设置为 public 方法，供更多的类使用，从而提高代码的复用性。
* 比较难给类起一个合适名字，很难用一个业务名词概括。
* 类中的大量方法都是集中操作类中的几个属性。

单一职责原则通过避免设计大而全的类，避免将不相关的功能耦合在一起，来提高类的内聚性。同时，类职责单一，类依赖的和被依赖的其他类也会变少，减少了代码的耦合性，以此来实现代码的高内聚、低耦合。但是，如果拆分得过细，实际上会适得其反，反倒会降低内聚性，也会影响代码的可维护性。

不管是应用设计原则还是设计模式，最终的目的还是提高代码的可读性、可扩展性、复用性、可维护性等。在考虑应用某一个设计原则是否合理的时候，也可以以此作为最终的考量标准。
